package com.gitee.wsl.struct.command.model.parameters.arguments

import com.gitee.wsl.struct.command.model.command.BaseCliktCommand
import com.gitee.wsl.struct.command.completion.CompletionCandidates

import com.gitee.wsl.struct.command.output.HelpFormatter.ParameterHelp
import com.gitee.wsl.struct.command.model.parameters.BaseOption
import com.gitee.wsl.struct.command.model.parameters.BaseOptionDelegate
import com.gitee.wsl.struct.command.model.parameters.arguments.delegate.ProcessedArgumentImpl
import com.gitee.wsl.struct.command.model.parameters.arguments.delegate.RawArgument
import com.gitee.wsl.struct.command.model.parameters.types.int


/**
 * A positional parameter to a command.
 *
 * Arguments can take any number of values.
 */
interface Argument : BaseOption<List<String>, ParameterHelp.Argument> {
    /** The metavar for this argument. */
    val name: String

    /**
     * The number of values that this argument takes.
     *
     * Negative [nvalues] indicates a variable number of values. Cannot be 0.
     */
    val nvalues: Int

    /** If true, an error will be thrown if this argument is not given on the command line. */
    val required: Boolean

    /**
     * The description of this argument.
     *
     * It's usually better to leave this null and describe options in the usage line of the command instead.
     */
    //fun help(context: Context): String

    /** Extra information about this argument to pass to the help formatter. */
    //val helpTags: Map<String, String>

    /** Optional set of strings to use when the user invokes shell autocomplete on a value for this argument. */
    //val completionCandidates: CompletionCandidates get() = CompletionCandidates.None

    /** Information about this argument for the help output. */
    //fun parameterHelp(context: Context): ParameterHelp.Argument?

    /**
     * Called after this command's argv is parsed to transform and store the argument's value.
     *
     * You cannot refer to other parameter values during this call, since they might not have been
     * finalized yet.
     *
     * @param context The context for this parse
     * @param values A possibly empty list of values provided to this argument.
     */
    //fun finalize(context: Context, values: List<String>)

    /**
     * Called after all of a command's parameters have been [finalize]d to perform validation of the final value.
     */
    //fun postValidate(context: Context)
}

/** An argument that functions as a property delegate */
/*interface ArgumentDelegate<out T> :
    Argument,
    ReadOnlyProperty<BaseCliktCommand<*>, T>,
    PropertyDelegateProvider<BaseCliktCommand<*>, ReadOnlyProperty<BaseCliktCommand<*>, T>> {
    *//**
     * The value for this argument.
     *
     * @throws IllegalStateException if this property is accessed before [finalize] is called.
     *//*
    val value: T

    override fun getValue(thisRef: BaseCliktCommand<*>, property: KProperty<*>): T = value
}*/

interface ArgumentDelegate<out T> : Argument,
    BaseOptionDelegate<List<String>, ParameterHelp.Argument, BaseCliktCommand<*>, T>


/**
 * Create a property delegate argument.
 *
 * The order that these delegates are created is the order that arguments must appear. By default, the
 * argument takes one value and throws an error if no value is given. The behavior can be changed with
 * functions like [int] and [optional].
 *
 * @param name The metavar for this argument. If not given, the name is inferred form the property name.
 * @param help The description of this argument for help output.
 * @param helpTags Extra information about this option to pass to the help formatter
 */
@Suppress("UnusedReceiverParameter")
fun BaseCliktCommand<*>.argument(
    name: String = "",
    help: String = "",
    helpTags: Map<String, String> = emptyMap(),
    completionCandidates: CompletionCandidates? = null,
): RawArgument {
    return ProcessedArgumentImpl(
        name = name,
        nvalues = 1,
        required = true,
        helpGetter = { help },
        helpTags = helpTags,
        explicitCompletionCandidates = completionCandidates,
        transformValue = { it },
        transformAll = defaultAllProcessor(),
        transformValidator = defaultValidator()
    )
}


